#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdbool.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <string.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <stdint.h>
#include <stdbool.h>
#include <linux/input.h>

enum DIR{
	UP, LEFT, DOWN, RIGHT
};

// BMP格式头规范
struct bitmap_header {
	int16_t type;
	int32_t size; // 图像文件大小
	int16_t reserved1;
	int16_t reserved2;
	int32_t offbits; // bmp图像数据偏移量
} __attribute__((packed));

struct bitmap_info {
	int32_t size;   // 本结构大小
	int32_t width;  // 图像宽
	int32_t height; // 图像高
	int16_t planes;
	
	int16_t bit_count; // 色深
	int32_t compression;
	int32_t size_img; // bmp数据大小，必须是4的整数倍
	int32_t X_pel;
	int32_t Y_pel;
	int32_t clrused;
	int32_t clrImportant;
} __attribute__((packed));

// 以下结构体不一定存在于BMP文件中，除非：
// bitmap_info.compression为真
struct rgb_quad {
	int8_t blue;
	int8_t green;
	int8_t red;
	int8_t reserved;
} __attribute__((packed));

int32_t width, height, bpp, size;

int edge = 0x00888888;

int figure[4][4] = {0};
int Coord_x[8] = {185, 285, 295, 395, 405, 505, 515, 615};
int Coord_y[8] = {25, 125, 135, 235, 245, 345, 355, 455};
int edge_len = 100;

int grade = 0;
int blank = 16;
bool is_fail = false;

bool is_show = true;

bool inHorizontal(int row){
	for(int i = 1, j = 2; i <= 5; i += 2, j += 2){
		if(row >= Coord_y[i] && row < Coord_y[j]){
			return true;
		}
	}
	return false;
}

bool inVertical(int row){
	for(int i = 1, j = 2; i <= 5; i += 2, j += 2){
		if(row >= Coord_x[i] && row < Coord_x[j]){
			return true;
		}
	}
	return false;
}

void showFrame(char *p){
	for(int row = 0; row < 480; row++){
		// 顶部 和 底部 边框
		if(row < Coord_y[0] || row >= Coord_y[6] + 100){
			for(int col = 160; col < 640; col++){
				memcpy(p + 4 * (row * 800 + col), &edge, 4);
				memcpy(p + 4 * (row * 800 + col) + size, &edge, 4);
			}
		}
		// 分割行 边框
		else if(inHorizontal(row)){
			for(int col = 160; col < 640; col++){
				if(inVertical(col)){
					continue;
				}
				memcpy(p + 4 * (row * 800 + col), &edge, 4);
				memcpy(p + 4 * (row * 800 + col) + size, &edge, 4);
			}
		}	
		// 数据行 边框
		else {
			for(int col = 160; col < 640; col++){
				if(inVertical(col) || col < 185 || col >= 615){
					memcpy(p + 4 * (row * 800 + col), &edge, 4);
					memcpy(p + 4 * (row * 800 + col) + size, &edge, 4);
				}
			}
		}
	}
}

// 获取bmp文件名
void getBmpName(int now, char *path_name){
	char name[15];
	int get_num = sprintf(name, "./num/%d.bmp", now);
	if(get_num > 0){
		strcpy(path_name, name);
	}
	return;
}

void showCell(char *now){
	blank = 0;
	for(int row = 0; row < 4; row++){
		for(int col = 0; col < 4; col++){
			if(figure[row][col] != 0){
				grade = (figure[row][col] > grade) ? figure[row][col] : grade;
				char name[15];
				getBmpName(figure[row][col], name);
//				printf("%d %d文件 %s\n", row, col, name);
				
				// 打开BMP文件
				int bmpFd = open(name, O_RDONLY);
				
				// 读取BMP格式头，获取图片信息
				struct bitmap_header header;
				struct bitmap_info info;
				struct rgb_quad quad;
				
				read(bmpFd, &header, sizeof(header));
				read(bmpFd, &info, sizeof(info));
				
				if (info.compression != 0) {
					read(bmpFd, &quad, sizeof(quad));
					fprintf(stderr, "read quad! \n");
				}
				
				// 读取BMP文件信息
				char *bmpbuf = malloc(3 * 100 * 100);
				int lineByte = 3 * 100;
				read(bmpFd, bmpbuf, 3 * 100 * 100);
				
				// 映射到屏幕
				for(int i = Coord_y[2 * row], r = 99; r >= 0; i++, r--){
					for(int j = Coord_x[2 * col], c = 0; c < lineByte; j++, c += 3){
						int index = r * lineByte + c; 
						int blue = bmpbuf[index];
						int green = bmpbuf[index + 1];  
						int red = bmpbuf[index + 2];
						int color = 0 << 24 | (red << 16) | (green << 8) | blue;
						int offset = (i * 800 + j) * 4;
						memcpy(now + offset, &color, 4);
					}
				}
				
				close(bmpFd);
			} else {
				blank++;
				for(int i = Coord_y[2 * row]; i < Coord_y[2 * row + 1]; i++){
					for(int j = Coord_x[2 * col]; j < Coord_x[2 * col + 1]; j++){
						int color = 0x00FFFFFF;
						int offset = (i * 800 + j) * 4;
						memcpy(now + offset, &color, 4);
					}
				}
			}
		}
	}
}



enum DIR getDir(){
	int tsFd = open("/dev/input/event0", O_RDWR);
	struct input_event tinfo;
	int x = 0, y = 0, tmpx = 0, tmpy = 0;
	while(1){
		int read_ret = read(tsFd, &tinfo, sizeof(tinfo));
		if(read_ret < 0){
			perror("打开触摸屏失败\n");
			break;
		}
		
		if(tinfo.type == EV_ABS){
			if(tinfo.code == ABS_X){
				tmpx = tinfo.value;
			}
			if(tinfo.code == ABS_Y){
				tmpy = tinfo.value;
			}
		}
		
		if(tinfo.type == EV_KEY && tinfo.code == BTN_TOUCH){
			if(tinfo.value > 0){
				x = tmpx;
				y = tmpy;
				printf("1、x = %d  y = %d\n", tmpx, tmpy);
			} else {
				x -= tmpx;
				y -= tmpy;
				printf("2、x = %d  y = %d\n", tmpx, tmpy);
				break;
			}
		}
	}
	
	close(tsFd);
	
	if(x >= 0){
		if(y >= 0){
			return (y < x) ? LEFT : UP;
		} else {
			y *= (-1);
			return (y < x) ? LEFT : DOWN;
		}
	}else {
		x *= (-1);
		if(y >= 0){
			return (y < x) ? RIGHT : UP;
		} else {
			y *= (-1);
			return (y < x) ? RIGHT : DOWN;
		}
	}
}

void goUp(){
	for(int col = 0; col < 4; col++){
		for(int up = 0; up < 4 ; up++){
			int down = up + 1;
			while(figure[down][col] == 0){
				down++;
				if(down >= 4){
					break;
				}
			}
			if(down < 4){
				if(figure[up][col] != 0){
					if(figure[up][col] == figure[down][col]){
						figure[up][col] *= 2;	
						figure[down][col] = 0;
						blank++;
						is_show = true;
					}
				} else {
					figure[up][col] = figure[down][col];
					figure[down][col] = 0;
					up--;
					is_show = true;
				}
			}
		}
	}
}

void goDown(){
	for(int col = 0; col < 4; col++){
		for(int down = 3; down >= 0; down--){
			int up = down - 1;
			while(figure[up][col] == 0){
				up--;
				if(up < 0){
					break;
				}
			}
			if(up >= 0){
				if(figure[down][col] != 0){
					if(figure[down][col] == figure[up][col]){
						figure[down][col] *= 2;	
						figure[up][col] = 0;
						blank++;
						is_show = true;
					}
				} else {
					figure[down][col] = figure[up][col];
					figure[up][col] = 0;
					down++;
					is_show = true;
				}
			}
		}
	}
}

void goLeft(){
	for(int row = 0; row < 4; row++){
		for(int left = 0; left < 4 ; left++){
			int right = left + 1;
			while(figure[row][right] == 0){
				right++;
				if(right >= 4){
					break;
				}
			}
			if(right < 4){
				if(figure[row][left] != 0){
					if(figure[row][left] == figure[row][right]){
						figure[row][left] *= 2;	
						figure[row][right] = 0;
						blank++;
						is_show = true;
					}
				} else {
					figure[row][left] = figure[row][right];
					figure[row][right] = 0;
					left--;
					is_show = true;
				}
			}
		}
	}
}

void goRight(){
	for(int row = 0; row < 4; row++){
		for(int right = 3; right >= 0 ; right--){
			int left = right - 1;
			while(figure[row][left] == 0){
				left--;
				if(left < 0){
					break;
				}
			}
			if(left >= 0){
				if(figure[row][right] != 0){
					if(figure[row][right] == figure[row][left]){
						figure[row][right] *= 2;	
						figure[row][left] = 0;
						blank++;
						is_show = true;
					}
				} else {
					figure[row][right] = figure[row][left];
					figure[row][left] = 0;
					right++;
					is_show = true;
				}
			}
		}
	}
}

void updateFig(){
	enum DIR dir = getDir();
	is_show = false;
	switch (dir) {
	case UP:
		printf("UP\n");
		goUp();
		break;
	case LEFT:
		printf("LEFT\n");
		goLeft();
		break;
	case DOWN:
		printf("DOWN\n");
		goDown();
		break;
	case RIGHT:
		printf("RIGHT\n");
		goRight();
		break;
		default:
			break;
	}
}

void createNum(int n){
	if(blank == 0){
		is_fail = true;
		return;
	}
	int row, col;
	do{
		row = (rand() + n) % 4;
		col = rand() % 4;
	}while(figure[row][col] != 0);
	int factor = rand() % 5;
	factor = (factor == 4) ? 4 : 2;
	figure[row][col] = factor;
	return;
}

int main(int argc, char *argv[]){
	srand((unsigned int)time(NULL));
	
	int lcdFd = open("/dev/fb0", O_RDWR|O_EXCL);
	struct fb_var_screeninfo vinfo;
	ioctl(lcdFd, FBIOGET_VSCREENINFO, &vinfo);
	
	width = vinfo.xres;
	height = vinfo.yres;
	bpp = vinfo.bits_per_pixel;
	size = width * height * bpp/8;
	
	char *p = mmap(NULL, 2 * size, PROT_WRITE, MAP_SHARED, lcdFd, 0);
	memset(p, 0x00FFFFFF, 2 * size);
	
	vinfo.xoffset = 0;
	vinfo.yoffset = 480;
	ioctl(lcdFd, FBIOPAN_DISPLAY, &vinfo);
	
	for(int i = 1; i <= 3;i++){
		createNum(i);
	}
	showFrame(p);
	for(int n = 0;; n++){
		char *tmp = p + (n % 2) * size;
		
		if(is_show){	
			createNum(n);
		}else {
			is_fail = (blank == 0) ? true : false;
		}
		showCell(tmp);
		vinfo.xoffset = 0;
		vinfo.yoffset = 480*(n%2);
		ioctl(lcdFd, FBIOPAN_DISPLAY, &vinfo);
		
		if(is_fail){
			printf("你输了！\n");
			break;
		}
		if(grade >= 2048){
			printf("你赢了！\n");
			break;
		}
		
		updateFig();
		printf("n\n");
	}
	
	close(lcdFd);
}
